﻿/* 
   Copyright 2011 LUCA Studios LLC & John Evans

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

namespace LUCAStudios.Silverlight.Controls
{
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Markup;
    using System.Linq;


    /// <summary>
    /// A class used to display a item within a Menu control
    /// </summary>
    [TemplatePart(Name = SubMenuIdentifier, Type = typeof(Grid))]
    [TemplatePart(Name = MenuItemRoot, Type = typeof(Grid))]
    [ContentProperty("SubMenu")]
    public class MenuItem : MenuBase
    {
        private bool _firstLoad;
        private bool _mouseInSubMenu;

        private Grid _subMenuIdentifier;
        private Grid _menuItemRoot;

        private const string SubMenuIdentifier = "gridSubMenuIdentifier";
        private const string MenuItemRoot = "gridMenuItemWrapper";

        /// <summary>
        /// Constructor
        /// </summary>
        public MenuItem()
        {
            DefaultStyleKey = typeof(MenuItem);
        }

        /// <summary>
        /// Called when the LUCA control is loaded
        /// </summary>
        protected override void OnLoaded()
        {
            base.OnLoaded();

            //display the sub menu marker if a sub menu exists
            _subMenuIdentifier.Visibility = SubMenu != default(Menu) ? Visibility.Visible : Visibility.Collapsed;

            //update the enable/disable visual state on each load
            VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", true);

            //everything after this only needs to happen the first time the control is loaded
            if (_firstLoad) return;
            _firstLoad = true;

            //update the visual state whenever the event fires
            IsEnabledChanged += (_, args) => VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", true);

            //register for the "click" event on the control
            _menuItemRoot.MouseLeftButtonUp += (_, __) =>
                                        {
                                            if (SubMenu != null) return;

                                            //Fires the Selected event, if subscribed to
                                            OnSelected();

                                            //executes the command delegate if it is set
                                            if (Command != default(ICommand) && Command.CanExecute(CommandParameter))
                                            {
                                                Command.Execute(CommandParameter ?? this);
                                            }

                                            //now bubble up to the parent menu event/command
                                            if (ParentMenuElement is Menu)
                                                ((Menu)ParentMenuElement).OnMenuItemSelected(this);

                                            LUCAMenuStrip.ResetLUCAMenuStrip(this);
                                        };

            if (SubMenu != null)
            {
                SubMenu.ParentMenuElement = this;

                //we are setting a flag to determine when the mouse is inside the SubMenu
                //for use by the gg.MouseLeave event.  This allows the user to smoothly
                //transition the mouse cursor from the MenuItem to the SubMenu
                SubMenu.MouseEnter += (_, __) => _mouseInSubMenu = true;
                SubMenu.MouseLeave += (_, __) => _mouseInSubMenu = false;
            }

            _menuItemRoot.MouseEnter += (_, __) =>
                                 {
                                     //close any other submenus that might be open
                                     if (((Menu)ParentMenuElement).CurrentSubMenu != null
                                         && ((Menu)ParentMenuElement).CurrentSubMenu != SubMenu)
                                     {
                                         ((Menu)ParentMenuElement).CurrentSubMenu.Remove();
                                     }

                                     if (SubMenu != null)
                                         ShowSubMenu();
                                 };

            //small delay gives time for the submenu mouseenter/leave to fire
            _menuItemRoot.MouseLeave += (_, __) => Observable
                                            .Timer(TimeSpan.FromSeconds(.3))
                                            .ObserveOnDispatcher()
                                            .Subscribe(o =>
                                                           {
                                                               if (SubMenu != null && !_mouseInSubMenu)
                                                               {
                                                                   HideSubMenu();
                                                               }
                                                           });
        }

        /// <summary>
        /// Called when the LUCA control is unloaded
        /// </summary>
        protected override void OnUnLoaded()
        {
            base.OnUnLoaded();
            VisualStateManager.GoToState(this, "Normal", true);
        }

        /// <summary>
        /// When overridden in a derived class, is invoked whenever application code or internal processes (such as a rebuilding layout pass) call <see cref="M:System.Windows.Controls.Control.ApplyTemplate"/>. In simplest terms, this means the method is called just before a UI element displays in an application. For more information, see Remarks.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _subMenuIdentifier = GetTemplateChild(SubMenuIdentifier) as Grid;
            _menuItemRoot = GetTemplateChild(MenuItemRoot) as Grid;
        }

        private void ShowSubMenu()
        {
            var pos = this.GetPosition(Application.Current.RootVisual);
            var aw = ActualWidth;

            if (LUCAMenuStripContext.MenuLayer.Children.Contains(SubMenu)) return;

            if (LUCAMenuStripContext.MenuLayer is Grid)
            {
                Grid.SetColumnSpan(SubMenu, 99);
                Grid.SetRowSpan(SubMenu, 99);
            }

            LUCAMenuStripContext.MenuLayer.Children.Add(SubMenu);

            SubMenu.AbsoluteTransformPositionTo(new Point(pos.X + aw - 3, pos.Y), Application.Current.RootVisual);
            ((Menu)ParentMenuElement).CurrentSubMenu = SubMenu;
        }

        private void HideSubMenu()
        {
            if (LUCAMenuStripContext.MenuLayer.Children.Contains(SubMenu))
            {
                SubMenu.Remove();
                ((Menu)ParentMenuElement).CurrentSubMenu = null;
            }
        }

        #region dependency properties
        /// <summary>
        /// A backing property for the Adornment dependency property
        /// </summary>
        public static readonly DependencyProperty AdornmentProperty =
            DependencyProperty.Register("Adornment", typeof(object), typeof(MenuItem), new PropertyMetadata(default(object)));

        /// <summary>
        /// Provides inclusion of adornment to MenuItem (the left-side icon).
        /// </summary>
        public object Adornment
        {
            get { return GetValue(AdornmentProperty); }
            set { SetValue(AdornmentProperty, value); }
        }

        /// <summary>
        /// A backing property for the Text dependency property
        /// </summary>
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(MenuItem), new PropertyMetadata("undefined"));

        /// <summary>
        /// The visible name of the MenuItem
        /// </summary>
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        /// <summary>
        /// A backing property for the AcceleratorKeys dependency property
        /// </summary>
        public static readonly DependencyProperty AcceleratorKeysProperty =
            DependencyProperty.Register("AcceleratorKeys", typeof(string), typeof(MenuItem), new PropertyMetadata(default(string)));

        /// <summary>
        /// Provides and area to display accelerator keys associated with the MenuItem
        /// </summary>
        public string AcceleratorKeys
        {
            get { return (string)GetValue(AcceleratorKeysProperty); }
            set { SetValue(AcceleratorKeysProperty, value); }
        }

        /// <summary>
        /// A backing property for the SubMenu dependency property
        /// </summary>
        public static readonly DependencyProperty SubMenuProperty =
            DependencyProperty.Register("SubMenu", typeof(Menu), typeof(MenuItem), new PropertyMetadata(default(Menu)));

        /// <summary>
        /// If set, the MenuItem will display the SubMenu on MouseEnter, rather than acting as a selection target itself.
        /// </summary>
        public Menu SubMenu
        {
            get { return (Menu)GetValue(SubMenuProperty); }
            set { SetValue(SubMenuProperty, value); }
        }

        /// <summary>
        /// A backing property for the Command dependency property
        /// </summary>
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand), typeof(MenuItem), new PropertyMetadata(default(ICommand)));

        /// <summary>
        /// Allows for binding of command delegate when the MenuItem is selected
        /// </summary>
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        /// <summary>
        /// A backing property for the CommandParameter dependency property
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter", typeof(object), typeof(MenuItem), new PropertyMetadata(default(object)));

        /// <summary>
        /// Parameter data to be passed to Command delegate.  Optional.
        /// </summary>
        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        #endregion
    }
}
